/*
 *  scripting_context.cc
 *  wonderland-project
 *
 *  Created by Ben Nolan on 14/10/08.
 *  Copyright 2008 Nolan Consulting Limited. All rights reserved.
 *
 */

#include "scripting_context.h"
#include <iostream>
#include <fstream>
#include <sstream>

namespace Wonderland{

  std::string ErrorDetails(v8::TryCatch* try_catch) {
    using namespace std;
    
    v8::HandleScope handle_scope;
    v8::String::Utf8Value exception(try_catch->Exception());
    v8::Handle<v8::Message> message = try_catch->Message();
    
    std::stringstream report;
    
    if (message.IsEmpty()) {
      report << "Exception\n  " << *exception << endl;

    } else {
      report << "Exception\n";

      //Print filename if possible
//      try{
//        v8::String::Utf8Value filename(message->GetScriptResourceName());
//        report << "  filename: " << *filename << endl;
//      }catch(...){
//      }
      
      int linenum = message->GetLineNumber();
      report << "  line num: " << linenum << endl;

      report << "  message: " << *exception << endl;

      // Print line of source code.
      v8::String::Utf8Value sourceline(message->GetSourceLine());
      report << *sourceline << endl;


      // Print wavy underline (GetUnderline is deprecated).
      int start = message->GetStartColumn();
      for (int i = 0; i < start; i++) {
        report << " ";
      }
      
      
      int end = message->GetEndColumn();
      for (int i = start; i < end; i++) {
        report << "^";
      }
      
      report << endl;
    }
    
    return report.str();
  }

  v8::Handle<v8::Value> Print(const v8::Arguments& args) {
    bool first = true;
    for (int i = 0; i < args.Length(); i++) {
      v8::HandleScope handle_scope;
      if (first) {
        first = false;
      } else {
        printf(" ");
      }
      v8::String::Utf8Value str(args[i]);
      printf("%s", *str);
    }
    printf("\n");
    return v8::Undefined();
  }

  ScriptingContext::ScriptingContext(){
    global_ = v8::ObjectTemplate::New();
    context_ = v8::Context::New(NULL, global_);

    context_->Enter();
    context_->Global()->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)->GetFunction());
  }

  void ScriptingContext::setGlobal(std::string str, v8::Handle<v8::Value> val){
    context_->Enter();
    context_->Global()->Set(v8::String::New(str.c_str()), val);
  }

  inline std::string slurp(std::ifstream& ifs) {
      std::stringstream sstr;
      sstr << ifs.rdbuf();
      return sstr.str();
  }

  bool ScriptingContext::loadAndEvaluate(std::string js){
    std::string result;

    std::cout << std::endl << std::endl << "Starting " << js << "... ";

    std::ifstream input(js.c_str());
    result = evaluate(slurp(input));
    
    if(result=="ok"){
      std::cout << "ok." << std::endl;
      return true;
    }else{
      std::cout << "failure." << std::endl;
      std::cout << result;
      return false;
    }
  }
  
  std::string ScriptingContext::evaluate(std::string js){
    context_->Enter();
    
    // Create a string containing the source
    v8::Handle<v8::String> source = v8::String::New(js.c_str());

    v8::TryCatch trycatch;

    // Compile the source code.
    v8::Handle<v8::Script> script = v8::Script::Compile(source);

    // Catch syntax error
    if (script.IsEmpty()) {
      return ErrorDetails(&trycatch);
    }
    
    // Run the script to get the result.
    v8::Handle<v8::Value> result = script->Run();
    
    // Catch a runtime error
    if (result.IsEmpty()){  
      return ErrorDetails(&trycatch);
    }

    return *(v8::String::AsciiValue(result));
  }

  
}